//
// Created by liaohailong on 2025/4/20.
//

#include "HelloDownloader.hpp"


HelloDownloader::HelloDownloader() : logger("HelloDownloaderVictor")
{
    logger.i("HelloDownloader::HelloDownloader(%p)", this);
}

HelloDownloader::~HelloDownloader()
{
    logger.i("HelloDownloader::~HelloDownloader(%p)", this);
}

std::shared_ptr<HelloDownloader::HelloDownloadCtx> HelloDownloader::newParams(
        const std::shared_ptr<HelloCacheManager> &cache,
        const std::shared_ptr<HelloHttpRequest> &request,
        const std::shared_ptr<HelloRange> &range
)
{
    const int fd = request->getConnFd();
    const std::string &url = request->getRealUrl();
    const std::shared_ptr<HelloCacheInfo> &cacheInfo = cache->getCacheInfo(url);

    std::shared_ptr<HelloDownloader::HelloDownloadCtx> params(
            new HelloDownloader::HelloDownloadCtx());
    params->cacheInfo = cacheInfo;
    params->fd = fd;
    params->url = url;
    params->sendHeader = true;
    params->write2index = true;
    params->range = range;
    params->ofs = nullptr;

    // 是否为请求部分资源
    bool partial = range->available(); // 是否请求部分资源?
    if (partial)
    {
        // 请求部分资源,组装 range 字段
        std::string header_range = "bytes=";
        header_range += std::to_string(range->start);
        header_range += "-";
        if (range->end > 0)
        {
            header_range += std::to_string(range->end);
        }
        params->requestHeaders["Range"] = header_range;
        logger.i("header_range: %s", header_range.c_str());
    }

    return params;
}

bool HelloDownloader::download(const std::shared_ptr<HelloDownloader::HelloDownloadCtx> &params)
{
    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();

    if (!curl)
    {
        logger.e("curl_easy_init error: curl is null?");
        return false;
    }

    params->logger = &logger;
    int fd = params->fd;

    // curl add custom headers
    struct curl_slist *chunk = nullptr;
    for (const auto &item: params->requestHeaders)
    {
        const std::string &key = item.first;
        const std::string &value = item.second;

        std::string header;
        header += key;
        header += ": ";
        header += value;

        chunk = curl_slist_append(chunk, header.c_str());
    }

    const std::string &url = params->url;
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* set our custom set of headers */
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);

    // 跳过HTTPS证书认证
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);

    // 设置 header 回调
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerCallback);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, params.get());

    // 设置 body 写入回调
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, params.get());

    res = curl_easy_perform(curl);
    curl_easy_cleanup(curl);
    /* free the custom headers */
    curl_slist_free_all(chunk);

    if (res != CURLE_OK)
    {
        logger.e("client[%d] curl_easy_perform failed: %s", fd, curl_easy_strerror(res));
    }
    return res == CURLE_OK;
}

size_t HelloDownloader::headerCallback(char *buffer, size_t size, size_t nitems, void *userdata)
{
    size_t totalSize = size * nitems;
    std::string headerLine(buffer, totalSize);
    auto params = reinterpret_cast<HelloDownloader::HelloDownloadCtx *>(userdata);

    // 原封不动地保存 header，每次回调都是一行（包括状态行和每一个 header）
    params->responseHeaderLines.append(buffer, totalSize);

    return totalSize;
}

size_t HelloDownloader::writeCallback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    auto params = reinterpret_cast<HelloDownloader::HelloDownloadCtx *>(userdata);
    Logger *logger = params->logger;
    int fd = params->fd;

    // 获取源服务器的headers后中转给客户端
    if (params->sendHeader)
    {
        params->sendHeader = false; // 仅处理一次

        const std::string &header = params->responseHeaderLines;
        // 先转发 header 给客户端
        ssize_t writen = write(params->fd, header.c_str(), header.size());
        logger->i("[NETWORK]Response to client[%d] writen[%d]: \n%s", params->fd, writen,
                  header.c_str());
        if (writen != header.size())
        {
            logger->e("send headers to client fd[%d] error writen[%d] != totalSize[%d]",
                      fd, writen, header.size());
            return 0;
        }

    }
    if (params->write2index)
    {
        params->write2index = false; // 仅处理一次

        // 把我们收集到的 response headers 缓存本地
        bool success = params->cacheInfo->write2Index(params->responseHeaderLines);
        if (!success)
        {
            const std::string &url = params->url;
            logger->e("cannot write headers to index file? url[%s]", url.c_str());
            return 0;
        }
    }

    size_t totalSize = size * nmemb;

    // 先转回给客户端
    ssize_t writen = write(fd, ptr, totalSize);
//    logger->i("send to client[%d] written[%d]", connFd, writen);
    if (writen != totalSize)
    {
        params->logger->e("send body to client fd[%d] error writen[%d] != totalSize[%d]]",
                          fd, writen, totalSize);
        return 0;
    }

    // 判断是否需要缓存本地
    if (params->ofs)
    {
        params->ofs->write(ptr, static_cast<std::streamsize>(totalSize));
    }

    return totalSize;
}